/*
 * Copyright (c) 2025 Huawei Technologies Co., Ltd.
 * openUBMC is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *         http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */

#include <gtest/gtest.h>

#include <imu/imu.h>
#include <ipmb/ipmb.h>
#include <mc/engine/service.h>
#include <mc/variant.h>
#include <mc/variant.h>
#include <mc/common.h>
#include <test_utilities/test_base.h>
#include <memory>
#include <string>
#include <vector>

namespace test_imu {

mc::variant make_ipmi_variant(int completion_code, const std::vector<uint8_t>& data) {
    mc::variants payload;
    for (auto byte : data) {
        payload.push_back(static_cast<int64_t>(byte));
    }

    mc::variants result;
    result.push_back(static_cast<int64_t>(completion_code));
    result.push_back(mc::variant(payload));
    return mc::variant(result);
}

mc::variant make_failure_variant(int completion_code) {
    mc::variants empty_payload;
    mc::variants result;
    result.push_back(static_cast<int64_t>(completion_code));
    result.push_back(mc::variant(empty_payload));
    return mc::variant(result);
}

class test_interface : public mc::engine::interface<test_interface> {
    public:
        MC_INTERFACE("bmc.kepler.IpmiCore")
    
        //a{ss}yyyyyay
        mc::variant Request(
            const mc::dict& ctx,              // a{ss} - 上下文字典
            uint8_t channel_type,              // y - 通道类型
            uint8_t instance,                  // y - 实例序号
            uint8_t net_fn,                   // y - 网络功能码
            uint8_t lun,                      // y - 逻辑单元号
            uint8_t cmd,                      // y - 命令字
            const std::vector<uint8_t>& data  // ay - 命令参数数据
        ) {
            ilog("test_ipmb_interface: Request");
            mc::variants result = {mc::variant(0), mc::variant(
                std::vector<uint8_t>{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F})};
            return mc::variant(result);
        }
    };
    
    class test_object : public mc::engine::object<test_object> {
    public:
        MC_OBJECT(test_object, "bmc.kepler.IpmiCore", "/bmc/kepler/IpmiCore", (test_interface))
    
        test_interface m_test_interface;
    };
    
    class test_service : public mc::engine::service {
    public:
        test_service(const std::string& name) : mc::engine::service(name) {
        }
    };
class imu_test : public mc::test::TestWithEngine {
protected:
static void SetUpTestSuite() {
    TestWithEngine::SetUpTestSuite();
    m_service.init();
    m_service.start();
    m_obj = test_object::create();
    m_obj->set_object_path("/bmc/kepler/IpmiCore");
    m_obj->set_object_name("bmc.kepler.IpmiCore");
    m_service.register_object(m_obj);
}

static void TearDownTestSuite() {
    TestWithEngine::TearDownTestSuite();
}

void SetUp() override {
    m_imu = std::make_unique<dev::imu>(&m_service);
}

void TearDown() override {
    m_imu.reset();
}

static test_service m_service;
static mc::shared_ptr<test_object> m_obj;
std::unique_ptr<dev::imu> m_imu;
};

test_service imu_test::m_service{"bmc.kepler.ipmi_core"};
mc::shared_ptr<test_object> imu_test::m_obj;
} // namespace test_imu

// 添加反射元数据定义（必须在命名空间外）
MC_REFLECT(test_imu::test_interface, (Request))
MC_REFLECT(test_imu::test_object, ((m_test_interface, "bmc.kepler.IpmiCore")))

using namespace test_imu;

TEST_F(imu_test, constructor_requires_service) {
    EXPECT_NE(m_imu, nullptr);
    EXPECT_THROW(dev::imu(nullptr), mc::invalid_argument_exception);
}



TEST_F(imu_test, parse_pmu_pci_response_basic) {
    std::vector<uint8_t> payload = {0xDB, 0x07, 0x00, 0x11, 0x22, 0x33, 0x44};

    auto parsed = m_imu->parse_pmu_pci_response(payload, 4);

    EXPECT_EQ(parsed, (std::vector<uint8_t>{0x11, 0x22, 0x33, 0x44}));
}

TEST_F(imu_test, parse_pmu_pci_response_handles_short_data) {
    std::vector<uint8_t> payload = {0xDB, 0x07, 0x00, 0x55};
    auto                 parsed  = m_imu->parse_pmu_pci_response(payload, 4);
    EXPECT_EQ(parsed, (std::vector<uint8_t>{0x55}));
}

TEST_F(imu_test, parse_pmu_pci_response_empty_guard) {
    std::vector<uint8_t> empty_payload;
    auto                 parsed = m_imu->parse_pmu_pci_response(empty_payload, 4);
    EXPECT_TRUE(parsed.empty());
}

TEST_F(imu_test, parse_pmu_pci_response_header_too_short) {
    std::vector<uint8_t> payload = {0xAA, 0xBB};
    auto                 parsed  = m_imu->parse_pmu_pci_response(payload, 2);
    EXPECT_TRUE(parsed.empty());
}


TEST_F(imu_test, send_ipmi_request_invalid_input) {
    std::string response;
    EXPECT_FALSE(m_imu->send_ipmi_request(nullptr, 0, response));
}

TEST_F(imu_test, parse_pmu_pci_response_read_length_4) {
    std::vector<uint8_t> payload = {0xDB, 0x07, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66};
    auto                 parsed  = m_imu->parse_pmu_pci_response(payload, 4);
    EXPECT_EQ(parsed.size(), 4);
    EXPECT_EQ(parsed, (std::vector<uint8_t>{0x11, 0x22, 0x33, 0x44}));
}

TEST_F(imu_test, parse_pmu_pci_response_read_length_3) {
    std::vector<uint8_t> payload = {0xDB, 0x07, 0x00, 0x11, 0x22, 0x33, 0x44};
    auto                 parsed  = m_imu->parse_pmu_pci_response(payload, 3);
    EXPECT_EQ(parsed.size(), 3);
    EXPECT_EQ(parsed, (std::vector<uint8_t>{0x11, 0x22, 0x33}));
}

TEST_F(imu_test, parse_pmu_pci_response_read_length_exceeds_available) {
    std::vector<uint8_t> payload = {0xDB, 0x07, 0x00, 0x11, 0x22};
    auto                 parsed  = m_imu->parse_pmu_pci_response(payload, 10);
    EXPECT_EQ(parsed.size(), 2);  // 只能读取2个字节
    EXPECT_EQ(parsed, (std::vector<uint8_t>{0x11, 0x22}));
}

TEST_F(imu_test, parse_pmu_pci_response_read_length_zero) {
    std::vector<uint8_t> payload = {0xDB, 0x07, 0x00, 0x11, 0x22, 0x33};
    auto                 parsed  = m_imu->parse_pmu_pci_response(payload, 0);
    EXPECT_TRUE(parsed.empty());
}

TEST_F(imu_test, parse_pmu_pci_response_exactly_3_bytes) {
    std::vector<uint8_t> payload = {0xDB, 0x07, 0x00};
    auto                 parsed  = m_imu->parse_pmu_pci_response(payload, 1);
    EXPECT_TRUE(parsed.empty());  // 只有头部，没有数据
}

TEST_F(imu_test, destructor_does_not_crash) {
    {
        dev::imu local_imu(&m_service);
    }
    // 析构函数应该正常执行，不应该崩溃
    EXPECT_TRUE(true);
}

// ========================= send_ipmi_request 测试 =========================

TEST_F(imu_test, send_ipmi_request_nullptr_data) {
    std::string response;
    EXPECT_FALSE(m_imu->send_ipmi_request(nullptr, 10, response));
}

TEST_F(imu_test, send_ipmi_request_zero_length) {
    uint8_t data[] = {0x01, 0x02};
    std::string response;
    EXPECT_FALSE(m_imu->send_ipmi_request(data, 0, response));
}

TEST_F(imu_test, send_ipmi_request_valid_data) {
    uint8_t data[] = {0x01, 0x02, 0x03};
    std::string response;
    bool result = m_imu->send_ipmi_request(data, sizeof(data), response);
    EXPECT_TRUE(result); 
}

// ========================= send_pmu_pci_request 测试 =========================

TEST_F(imu_test, send_pmu_pci_request_is_local_true) {
    dev::imu_pcie_info pcie_info = {
        .is_local     = true,
        .cpu_id       = 0x05,
        .address      = 0x00,
        .function_num = 0x01,
        .device_num   = 0x02,
        .bus_num      = 0x03,
        .read_length  = 4,
    };
    auto result = m_imu->send_pmu_pci_request(pcie_info);
    EXPECT_TRUE(result.has_value()); 
}

TEST_F(imu_test, send_pmu_pci_request_is_local_false) {
    dev::imu_pcie_info pcie_info = {
        .is_local     = false,
        .cpu_id       = 0x05,
        .address      = 0x2C,
        .function_num = 0x02,
        .device_num   = 0x03,
        .bus_num      = 0x04,
        .read_length  = 3,
    };
    auto result = m_imu->send_pmu_pci_request(pcie_info);
    EXPECT_TRUE(result.has_value()); 
}

TEST_F(imu_test, send_pmu_pci_request_read_length_4) {
    dev::imu_pcie_info pcie_info = {
        .is_local     = false,
        .cpu_id       = 0x01,
        .address      = 0x00,
        .function_num = 0x00,
        .device_num   = 0x00,
        .bus_num      = 0x00,
        .read_length  = 4,
    };
    auto result = m_imu->send_pmu_pci_request(pcie_info);
    EXPECT_TRUE(result.has_value()); // 在没有真实 IPMB 后端的情况下，预期失败
}

TEST_F(imu_test, send_pmu_pci_request_read_length_not_4) {
    dev::imu_pcie_info pcie_info = {
        .is_local     = false,
        .cpu_id       = 0x01,
        .address      = 0x00,
        .function_num = 0x00,
        .device_num   = 0x00,
        .bus_num      = 0x00,
        .read_length  = 2, // 不是4，应该使用 length = 4
    };
    auto result = m_imu->send_pmu_pci_request(pcie_info);
    EXPECT_TRUE(result.has_value()); // 在没有真实 IPMB 后端的情况下，预期失败
}

TEST_F(imu_test, send_pmu_pci_request_cpu_id_mask) {
    // 测试 CPU ID 掩码处理
    dev::imu_pcie_info pcie_info_local = {
        .is_local     = true,
        .cpu_id       = 0x0F, // 测试掩码 0x07
        .address      = 0x00,
        .function_num = 0x00,
        .device_num   = 0x00,
        .bus_num      = 0x00,
        .read_length  = 4,
    };
    auto result_local = m_imu->send_pmu_pci_request(pcie_info_local);
    EXPECT_TRUE(result_local.has_value());

    dev::imu_pcie_info pcie_info_remote = {
        .is_local     = false,
        .cpu_id       = 0x0F, // 测试掩码 0x07
        .address      = 0x00,
        .function_num = 0x00,
        .device_num   = 0x00,
        .bus_num      = 0x00,
        .read_length  = 4,
    };
    auto result_remote = m_imu->send_pmu_pci_request(pcie_info_remote);
    EXPECT_TRUE(result_remote.has_value());
}

// ========================= get_pmu_status 测试 =========================

TEST_F(imu_test, get_pmu_status_basic) {
    dev::pmu_version_info version_info = {};
    bool result = m_imu->get_pmu_status(version_info);
    EXPECT_TRUE(result); 
}

// ========================= get_info_from_imu 测试 =========================

TEST_F(imu_test, get_info_from_imu_is_local_true) {
    dev::imu_pcie_device_req_t pcie_device_info = {
        .system_id        = 1,
        .socket_id        = 2,
        .bus              = 3,
        .device           = 4,
        .function         = 5,
        .pci_info_address = dev::VID_DID_ADDRESS,
        .rlen             = 4,
    };
    auto result = m_imu->get_info_from_imu(true, pcie_device_info);
    EXPECT_TRUE(result.has_value()); 
}

TEST_F(imu_test, get_info_from_imu_is_local_false) {
    dev::imu_pcie_device_req_t pcie_device_info = {
        .system_id        = 1,
        .socket_id        = 2,
        .bus              = 3,
        .device           = 4,
        .function         = 5,
        .pci_info_address = dev::SVID_SDID_ADDRESS,
        .rlen             = 4,
    };
    auto result = m_imu->get_info_from_imu(false, pcie_device_info);
    EXPECT_TRUE(result.has_value()); 
}

TEST_F(imu_test, get_info_from_imu_with_different_addresses) {
    // 测试不同的 PCI 信息地址
    dev::imu_pcie_device_req_t pcie_device_info_vid = {
        .system_id        = 1,
        .socket_id        = 0,
        .bus              = 0,
        .device           = 0,
        .function         = 0,
        .pci_info_address = dev::VID_DID_ADDRESS,
        .rlen             = 4,
    };
    auto result_vid = m_imu->get_info_from_imu(false, pcie_device_info_vid);
    EXPECT_TRUE(result_vid.has_value());

    dev::imu_pcie_device_req_t pcie_device_info_svid = {
        .system_id        = 1,
        .socket_id        = 0,
        .bus              = 0,
        .device           = 0,
        .function         = 0,
        .pci_info_address = dev::SVID_SDID_ADDRESS,
        .rlen             = 4,
    };
    auto result_svid = m_imu->get_info_from_imu(false, pcie_device_info_svid);
    EXPECT_TRUE(result_svid.has_value());

    dev::imu_pcie_device_req_t pcie_device_info_bus = {
        .system_id        = 1,
        .socket_id        = 0,
        .bus              = 0,
        .device           = 0,
        .function         = 0,
        .pci_info_address = dev::BUS_NUMBER_ADDRESS,
        .rlen             = 4,
    };
    auto result_bus = m_imu->get_info_from_imu(false, pcie_device_info_bus);
    EXPECT_TRUE(result_bus.has_value());
}

TEST_F(imu_test, get_info_from_imu_data_length_insufficient) {
    dev::imu_pcie_device_req_t pcie_device_info = {
        .system_id        = 1,
        .socket_id        = 0,
        .bus              = 0,
        .device           = 0,
        .function         = 0,
        .pci_info_address = dev::VID_DID_ADDRESS,
        .rlen             = 4,
    };
    auto result = m_imu->get_info_from_imu(false, pcie_device_info);
    EXPECT_TRUE(result.has_value()); 
}

// ========================= parse_pmu_pci_response 边界情况测试 =========================

TEST_F(imu_test, parse_pmu_pci_response_large_read_length) {
    std::vector<uint8_t> payload = {0xDB, 0x07, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55};
    auto                 parsed  = m_imu->parse_pmu_pci_response(payload, 100);
    EXPECT_EQ(parsed.size(), 5); // 只能读取5个字节（payload.size() - 3）
    EXPECT_EQ(parsed, (std::vector<uint8_t>{0x11, 0x22, 0x33, 0x44, 0x55}));
}

TEST_F(imu_test, parse_pmu_pci_response_single_byte) {
    std::vector<uint8_t> payload = {0xDB, 0x07, 0x00, 0xAA};
    auto                 parsed  = m_imu->parse_pmu_pci_response(payload, 1);
    EXPECT_EQ(parsed.size(), 1);
    EXPECT_EQ(parsed[0], 0xAA);
}

TEST_F(imu_test, parse_pmu_pci_response_two_bytes) {
    std::vector<uint8_t> payload = {0xDB, 0x07, 0x00, 0xAA, 0xBB};
    auto                 parsed  = m_imu->parse_pmu_pci_response(payload, 2);
    EXPECT_EQ(parsed.size(), 2);
    EXPECT_EQ(parsed, (std::vector<uint8_t>{0xAA, 0xBB}));
}

TEST_F(imu_test, parse_pmu_pci_response_maximum_data) {
    // 测试最大数据长度
    std::vector<uint8_t> payload(256, 0x00);
    payload[0] = 0xDB;
    payload[1] = 0x07;
    payload[2] = 0x00;
    for (size_t i = 3; i < payload.size(); ++i) {
        payload[i] = static_cast<uint8_t>(i);
    }
    auto parsed = m_imu->parse_pmu_pci_response(payload, 253);
    EXPECT_EQ(parsed.size(), 253); // 应该读取253个字节（256 - 3）
}
